package com.iwhalecloud.bss.kite.cucc.service.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import com.alibaba.fastjson.JSON;
import net.sf.json.JSONObject;

import org.apache.commons.codec.binary.Base64;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.xml.sax.SAXException;

import com.fasterxml.jackson.databind.util.JSONPObject;


public class RsaEncodeUtil {

	private static final String KEY_ALGORITHM = "RSA";
	private static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
	/** *//**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;

    /** *//**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

	/**
	 * 生成公钥和私钥 public 公钥(RSAPublicKey) private 私钥(RSAPrivateKey)
	 *
	 * @throws NoSuchAlgorithmException
	 *
	 */
	public static Map<String, String> generateRSAKeys() throws NoSuchAlgorithmException {
		HashMap<String, String> map = new HashMap<String, String>();
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(1024);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

		byte[] pk = publicKey.getEncoded();
		byte[] privk = privateKey.getEncoded();
		String strpk = new String(Base64.encodeBase64(pk));
		String strprivk = new String(Base64.encodeBase64(privk));

		map.put("public", strpk);
		map.put("private", strprivk);

		return map;
	}

	/**
	 * 生成公钥和私钥 public 公钥(RSAPublicKey) private 私钥(RSAPrivateKey)
	 *
	 * @throws NoSuchAlgorithmException
	 *
	 */
	public static Map<String, String> generateRSAKeysForWopay() throws NoSuchAlgorithmException {
		HashMap<String, String> map = new HashMap<String, String>();
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(2048);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

		byte[] pk = publicKey.getEncoded();
		byte[] privk = privateKey.getEncoded();
		String strpk = new String(Base64.encodeBase64(pk));
		String strprivk = new String(Base64.encodeBase64(privk));

		map.put("public", strpk);
		map.put("private", strprivk);

		return map;
	}


	/**
	 * 还原公钥，X509EncodedKeySpec 用于构建公钥的规范
	 *
	 * @param keyBytes
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 */
	public static PublicKey restorePublicKey(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
		X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(key));

		KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
		PublicKey publicKey = factory.generatePublic(x509EncodedKeySpec);
		return publicKey;
	}

	/**
	 * 还原私钥，PKCS8EncodedKeySpec 用于构建私钥的规范
	 *
	 * @param keyBytes
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeySpecException
	 */
	public static PrivateKey restorePrivateKey(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
		PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(key));
		KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateKey = factory.generatePrivate(pkcs8EncodedKeySpec);
		return privateKey;
	}

	/**
	 * 加密，三步走。
	 *
	 * @param key
	 * @param plainText
	 * @return
	 * @throws InvalidKeyException
	 * @throws NoSuchPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws BadPaddingException
	 * @throws IllegalBlockSizeException
	 */
	public static byte[] rsaPublicEncode(PublicKey key, byte[] encodeData) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {

		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, key);
        int inputLen = encodeData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(encodeData, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encodeData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        return decryptedData;
	}

	public static byte[] rsaPrivateEncode(PrivateKey key, byte[] encodeData) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {

		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		cipher.init(Cipher.ENCRYPT_MODE, key);
        int inputLen = encodeData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(encodeData, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encodeData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        return decryptedData;
	}

	/**
	 * 解密，三步走。
	 *
	 * @param key
	 * @param encodedText
	 * @return
	 * @throws NoSuchPaddingException
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 * @throws BadPaddingException
	 * @throws IllegalBlockSizeException
	 * @throws IOException
	 */
	public static byte[] rsaPrivateDecode(PrivateKey key, byte[] encryptedData) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {

		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, key);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
	}

	public static byte[] rsaPublicDecode(PublicKey key, byte[] encryptedData) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {

		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
		cipher.init(Cipher.DECRYPT_MODE, key);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
	}

	/**
	 * 签名
	 * @param map
	 * @param key
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws UnsupportedEncodingException
	 */
	public static String sha1Sign(Map<String,Object> map, String key) throws NoSuchAlgorithmException, UnsupportedEncodingException{
        ArrayList<String> list = new ArrayList<String>();
        for(Map.Entry<String,Object> entry:map.entrySet()){
        	// 对象不参与签名， 因为对象里面的不能控制排序; 对象转为String 类型时， 必定以 [ { 开始
        	 if(!"".equals(entry.getValue())&&!(entry.getValue().toString().startsWith("{\"")&&entry.getValue().toString().endsWith("\"}"))&&
        			 !(entry.getValue().toString().startsWith("[{\"")&&entry.getValue().toString().endsWith(("\"}]")))){
            		list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + key;

        //指定sha1算法
        MessageDigest digest = MessageDigest.getInstance("SHA-1");
        digest.update(result.getBytes("UTF-8"));
        //获取字节数组
        byte messageDigest[] = digest.digest();
        // Create Hex String
        StringBuffer hexString = new StringBuffer();
        // 字节数组转换为 十六进制 数
        for (int i = 0; i < messageDigest.length; i++) {
            String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
            if (shaHex.length() < 2) {
                hexString.append(0);
            }
            hexString.append(shaHex);
        }
        return hexString.toString().toUpperCase();
    }


	public static String sha1Sign(String data, String privateKey) throws Exception{
        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        KeyFactory keyf = KeyFactory.getInstance("RSA");
        PrivateKey priKey = keyf.generatePrivate(priPKCS8);
        Signature signature = Signature.getInstance("SHA1WithRSA");
        signature.initSign(priKey);
        signature.update(data.getBytes("GBK"));
        byte messageDigest[] = signature.sign();

        // Create Hex String
        StringBuffer hexString = new StringBuffer();
        // 字节数组转换为 十六进制 数
        for (int i = 0; i < messageDigest.length; i++) {
            String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
            if (shaHex.length() < 2) {
                hexString.append(0);
            }
            hexString.append(shaHex);
        }
        return hexString.toString().toUpperCase();
    }

	/**
     * 获取一定长度的随机字符串
     * @param length 指定字符串长度
     * @return 一定长度的字符串
     */
    public static String getRandomStringByLength(int length) {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
	 * xml转成map
	 * @param xmlString
	 * @return
	 * @throws ParserConfigurationException
	 * @throws IOException
     * @throws DocumentException
	 * @throws SAXException
	 */
	public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, DocumentException {
		if(!xmlString.startsWith("<xml")){
			xmlString = "<xml>"+xmlString+"</xml>";
		}
		Document document = DocumentHelper.parseText(xmlString);
		Element root = document.getRootElement();

		Map<String, Object> map = new HashMap<String, Object>();
		Iterator<Element> rootItor = root.elementIterator();

		while (rootItor.hasNext()) {
			Element tmpElement = rootItor.next();
			String name = tmpElement.getName();
			String text = tmpElement.getTextTrim();

			map.put(name, text);
		}

		return map;
    }

	public static InputStream getStringStream(String sInputString) throws UnsupportedEncodingException {
        ByteArrayInputStream tInputStringStream = null;
        if (sInputString != null && !sInputString.trim().equals("")) {
            tInputStringStream = new ByteArrayInputStream(sInputString.getBytes("UTF-8"));
        }
        return tInputStringStream;
    }

	/**
	 * map转成xml
	 * @param map
	 * @return
	 */
	public static String getXmlFromMap(Map<String,Object> map){
		if(map == null || map.size() == 0){
			return "";
		}

		StringBuilder builder = new StringBuilder();
		for(String key : map.keySet()){
			builder.append("<"+key+"><![CDATA["+map.get(key)+"]]>"+"</"+key+">");
		}

		return builder.toString();
	}

	/** json转换为map*/
	public static Map<String,Object> getMapFromJson(String json){
		return getMapFromJsonByFastJson(json);
//		Map<String, Object> map = new HashMap<String, Object>();
//		JSONObject jsonObject = JSONObject.fromObject(json);
//		 Iterator it = jsonObject.keys();
//         while (it.hasNext())
//         {
//             String key = String.valueOf(it.next());
//             Object value = jsonObject.get(key);
//             map.put(key, value);
//         }
//         System.out.println("Map = " + map);
//        return map;
	}

	/** map转为json*/
	public static String getJsonFromMap(Map<String,Object> map){
		return getJsonFromMapByFastJson(map);
//			JSONObject jsonObject = new JSONObject();
//			if(map == null || map.size() == 0){
//				return "";
//			}
//			jsonObject =JSONObject.fromObject(map);
//			return jsonObject.toString();
	}

	private static String getJsonFromMapByFastJson(Map<String, Object> map) {
		return com.alibaba.fastjson.JSONObject.toJSONString(map);
	}

	private static Map<String,Object> getMapFromJsonByFastJson(String json){
		return com.alibaba.fastjson.JSONObject.parseObject(json,Map.class);
	}
}
